package org.rcy.framework.data.config.mybatis;



import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.rcy.framework.data.config.DataSourceAutoConfiguration;
import org.rcy.framework.data.config.mybatis.plugins.SelectLimitPlugin;
import org.rcy.framework.data.dao.MapperHelperFactory;
import org.rcy.framework.data.datasource.DataSourceProperties;
import org.rcy.framework.data.datasource.Database;
import org.rcy.framework.data.id.IdGenerator;
import org.rcy.framework.data.id.IdWorker;
import org.rcy.framework.data.mybatis.DatabaseIdProviderImpl;
import org.rcy.framework.data.transaction.RcyDataSourceContext;
import org.rcy.framework.data.transaction.RcyDataSourceTransactionManager;
import org.rcy.framework.api.entity.TypeAliases;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import tk.mybatis.mapper.autoconfigure.SpringBootVFS;
import tk.mybatis.mapper.mapperhelper.MapperHelper;

import javax.sql.DataSource;

/**
 * @author 吴康桥
 * @Description
 * @date 2022/9/29 12:21
 */

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE+40)
@Import(DataSourceAutoConfiguration.class)
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties(MybatisProperties.class)
@MybatisMapperScan(lazyInitialization = true)
public class MybatisAutoConfiguration implements InitializingBean{
	private static Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

	@Autowired
	private MybatisProperties properties;
	@Autowired
	private ResourceLoader resourceLoader;
	@Autowired
	private DataSourceProperties dataSourceProperties;


	@Bean
	@ConditionalOnClass(name = {"org.rcy.framework.data.id.IdWorker"})
	public IdGenerator<Long> idGenerator() {
		return new IdWorker();
	}

	@Bean
	public DatabaseIdProvider databaseIdProvider() {
		return new DatabaseIdProviderImpl();
	}

	@Bean
	public RcyDataSourceContext dataSourceContext() {
		RcyDataSourceContext context = new RcyDataSourceContext(this.dataSourceProperties.getMaxTransactionTime(), this.dataSourceProperties.getMaxLimit(), this.dataSourceProperties.getMaxLogTime());
		if (this.dataSourceProperties.isRegisterMbeans()) {
			context.handleMBeans();
		}

		return context;
	}

	@Bean
	public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource,DatabaseIdProvider databaseIdProvider,RcyDataSourceContext dataSourceContext) throws Exception {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		factory.setDataSource(dataSource);
		factory.setVfs(SpringBootVFS.class);
		String configLocation = properties.getConfigLocation();
		factory.setConfigLocation(resourceLoader.getResource(configLocation));
		// factory.setPlugins(this.interceptors);
		factory.setDatabaseIdProvider(databaseIdProvider);
		factory.setTypeAliasesSuperType(TypeAliases.class);
		factory.setTypeAliasesPackage(properties.getTypeAliasesPackage());
		factory.setTypeAliases(properties.getTypeAliases());
		//factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
		//是指关键字处理策略
		String databaseId = databaseIdProvider.getDatabaseId(dataSource);
		if(dataSourceProperties.isUseKeywordWrap()) {
			logger.info("MybatisAutoConfiguration useKeywordWrap {}",dataSourceProperties.isUseKeywordWrap());
			MapperHelper mapperHelper = MapperHelperFactory.getMapperHelper();
			String getkeywordWrap = Database.getkeywordWrap(databaseId);
			mapperHelper.getConfig().setWrapKeyword(getkeywordWrap);
		}

		//添加最大返回行数限制,先仅支持mysql
		if(dataSourceContext.getMaxLimit() != 0 && Database.fromDatabaseId(databaseId) == Database.MYSQL) {
			logger.info("MybatisAutoConfiguration setMaxLimit {} maxLogTime {}",dataSourceContext.getMaxLimit(),dataSourceContext.getMaxLogTime());
			SelectLimitPlugin plugin = new SelectLimitPlugin(dataSourceContext);
			factory.setPlugins(plugin);
		}

		return factory;
	}

	@Bean("sqlSessionTemplate")
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		ExecutorType executorType = properties.getExecutorType();
		if (executorType != null) {
			return new SqlSessionTemplate(sqlSessionFactory, executorType);
		} else {
			return new SqlSessionTemplate(sqlSessionFactory);
		}
	}


	@Bean
	public DataSourceTransactionManager transactionManager(DataSource dataSource, RcyDataSourceContext dataSourceContext) {
		return (DataSourceTransactionManager)(this.dataSourceProperties.getMaxTransactionTime() > 0L ? new RcyDataSourceTransactionManager(dataSource, dataSourceContext) : new DataSourceTransactionManager(dataSource));
	}

	@Override
	public void afterPropertiesSet() throws Exception {

	}
}
